package tabletask

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
	"github.com/opensourceways/server-common-lib/utils"

	"cvevulner/cve-timed-task/db_models"
	"cvevulner/models"
)

const (
	pageSize     = 100
	statusPushed = 1
	sourceNG     = "ng"
)

// SyncPackages sync packages from ng.
func SyncPackages() {
	pkgs, err := getAllPackages()
	if err != nil {
		logs.Error("sync packages from ng failed:", err)
		return
	}

	db := orm.NewOrm()
	if err = db.Begin(); err != nil {
		logs.Error("sync packages begin transaction failed:", err)
		return
	}

	if err = db_models.DeleteYamlOpenEulerDataBySource(db, sourceNG); err != nil {
		logs.Error("sync packages delete data by source failed:", err)
		db.Rollback()

		return
	}

	now := time.Now()
	for _, v := range pkgs {
		goe := models.GitOpenEuler{
			PackageName: v.PackageName,
			Version:     strings.Join(v.PackageVersion, ","),
			CreateTime:  now,
			UpdateTime:  now,
			CpePackName: v.CpeName,
			Status:      statusPushed,
			Source:      sourceNG,
		}

		if _, err = db_models.InsertYamlData(&goe, db); err != nil {
			logs.Error("sync packages failed:", goe, err)
		}
	}

	db.Commit()
}

// Packages info about package.
type Packages struct {
	CpeName        string   `json:"cpe_name"`
	PackageName    string   `json:"package_name"`
	PackageVersion []string `json:"package_version"`
}

func getAllPackages() ([]Packages, error) {
	var pkgs []Packages
	page := 1
	for {
		result, err := getPackageFromNG(page)
		if err != nil {
			return nil, err
		}

		pkgs = append(pkgs, result...)

		if len(result) < pageSize {
			break
		}

		page++
	}

	return pkgs, nil
}

type response struct {
	Code string `json:"code"`
	Msg  string `json:"msg"`
	Data struct {
		Total int        `json:"total"`
		List  []Packages `json:"list"`
	} `json:"data"`
}

func getPackageFromNG(page int) ([]Packages, error) {
	url := fmt.Sprintf("%s/api/v1/packages?page=%d&per_page=%d&exclude_org=ascend",
		beego.AppConfig.String("cve-manager-ng::host"), page, pageSize)

	const maxRetry = 3
	cli := utils.NewHttpClient(maxRetry)
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}

	var resp response
	if _, err = cli.ForwardTo(req, &resp); err != nil {
		return nil, err
	}

	if resp.Msg != "" {
		return nil, errors.New(resp.Msg)
	}

	return resp.Data.List, nil
}
